package com.dsp.week11.utils;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.UUID;

@Component
@Slf4j
@Aspect
public class RedisLockAspect {

    @Resource
    private RedisLockUtil redisLockUtil;


    @Around("@annotation(redisLock)")
    public Object redisLock (ProceedingJoinPoint point, RedisLock redisLock) throws Throwable {
        String lockKey = redisLock.lockKey();
        String lockValue = UUID.randomUUID().toString().replaceAll("-","");
        log.info("lock ---> key: {} , value: {}",lockKey,lockValue);
        if (!redisLockUtil.lock(lockKey,lockValue,redisLock.lockTime())) {
            int tryCount = redisLock.tryCount();
            int sleepTime = redisLock.tryTime();
            int i = 1;
            do {
                tryCount --;
                Thread.sleep(sleepTime * 1000L);
                if (redisLockUtil.lock(lockKey,lockValue,redisLock.lockTime())) {
                    lockVale(redisLock,lockValue);
                    log.info(Thread.currentThread().getName()+"已经获取到锁，这是第{}次重试",i);
                    break;
                }
                log.info(Thread.currentThread().getName()+"获取锁失败，这是第{}次尝试拿锁",i);
                i++;
                if (tryCount == 0) {
                    throw new Exception(Thread.currentThread().getName()+"获取锁失败");
                }
            } while (tryCount > 0);
        }else{
            lockVale(redisLock,lockValue);
        }
        return point.proceed();
    }

    private void lockVale(RedisLock redisLock,String lockValue) throws NoSuchFieldException, IllegalAccessException {
        InvocationHandler h = Proxy.getInvocationHandler(redisLock);
        Field hField = h.getClass().getDeclaredField("memberValues");
        hField.setAccessible(true);
        Map<String, Object> memberValues = (Map<String, Object>) hField.get(h);
        System.out.println("--->" + memberValues.toString());
        memberValues.put("lockValue", lockValue);
    }


    @AfterReturning(value = "@annotation(redisLock)")
    public void afterReturning(JoinPoint joinPoint, RedisLock redisLock) throws Throwable {
        boolean unlock = redisLockUtil.unlock(redisLock.lockKey(),redisLock.lockValue());
        log.info("unlock key --> {}, {}, {}", redisLock.lockKey(),redisLock.lockValue(),unlock);
    }

}
